﻿using System.IO;
using System.Collections.Generic;
using System.Text.RegularExpressions;
namespace ANLP.Lexer
{
    public class LexerParser : Parser<LexerExpression>
    {
        TextWriter writer;
        public LexerParser(LexerLexer lexer, TextWriter writer)
            : base(lexer)
        {
            this.writer = writer;
            parameters.Add("Class", "Lexer");
            parameters.Add("Namespace", "ANLP");
            parameters.Add("Visibility", "internal");
            parameters.Add("Code", "");
        }

        /*
         * Regex    Code
         * ^%$Identifier Identifier
         * ^%%$ Code duplication ^%%$
         * <State>? Regex    Code
         */
        static LexerParser()
        {
            states = new int[][] { 
                new int[] { 0 },
                new int[] { -1 },
                new int[] { (int)Tokens.Regex, (int)Tokens.Code }, 
                new int[] { (int)Tokens.SinglePercent, -3, -3 }, 
                new int[] { (int)Tokens.DoublePercent, (int)Tokens.Code,(int)Tokens.DoublePercent}, 
                new int[] { (int)Tokens.StateStart,  (int)Tokens.Token, (int)Tokens.StateEnd, (int)Tokens.Regex, (int)Tokens.Code},
                new int[] { (int)Tokens.Token, },
                new int[]{(int)Tokens.MultipartToken, },
                new int[] { (int)Tokens.Token, (int)Tokens.Code }, 
                };

            rules = new int[][] { new int[] { -1 }, new int[] { 1, 2 }, new int[] { -2, -3, -4, -5, -8 }, new int[] { -6, -7 } };
        }

        private static int[][] states;

        private static int[][] rules;


        protected override int[][] States
        {
            get { return states; }
        }

        protected override int[][] Rules
        {
            get { return rules; }
        }

        private LexerExpression currentExpression;

        private List<LexerExpression> expressions = new List<LexerExpression>();

        protected override void DoAction(int state, int step, ParserExpression<LexerExpression>[] expressions)
        {
            switch (state)
            {
                case -1:
                    //EOF
                    GenerateClass();
                    break;
                case 2:
                case 8:
                    // Regex Code
                    switch (step)
                    {
                        //Regex
                        case 0:
                            this.expressions.Add(currentExpression = new LexerExpression(lexer.GetText()));
                            lexer.ChangeState(2);
                            break;
                        //Code
                        case 1:
                            currentExpression.Code = lexer.GetText();
                            lexer.ChangeState(0);
                            break;
                    }
                    break;
                case 3:
                    // ^%$Identifier Identifier
                    switch (step)
                    {
                        case 0:
                            lexer.ChangeState(3);
                            break;
                        case 1:
                            currentExpression = new LexerExpression(lexer.GetText());
                            break;
                        case 2:
                            SetParameter(currentExpression.Regex, lexer.GetText());
                            currentExpression = null;
                            break;
                        default:
                            lexer.ChangeState(0);
                            break;
                    }
                    break;
                case 4:
                    // ^%%$ Code ^%%$
                    switch (step)
                    {
                        case 0:
                            lexer.ChangeState(1);
                            break;
                        case 1:
                            SetParameter("Code", lexer.GetText());
                            break;
                        case 2:
                            lexer.ChangeState(0);
                            break;
                    }
                    break;
                case 5:
                    // <State> Regex    Code
                    switch (step)
                    {
                        case 1:
                            //State
                            currentExpression = new LexerExpression(lexer.GetText());
                            break;
                        case 3:
                            currentExpression = new LexerExpression(lexer.GetText()) { State = currentExpression.Regex };
                            break;
                        case 4:
                            currentExpression.Code = lexer.GetText();
                            break;
                        default:
                            break;
                    }
                    break;
            }
        }

        Regex stringOnlyRegex = new Regex(@"^([^([\]{}?*+]|\\[\]{}?*+])+$");
        Regex stringRegexEscape = new Regex(@"\\([\]{}?*+])");
        private void GenerateClass()
        {
            List<string> states = new List<string>();
            writer.WriteLine("using System.Collections.Generic;");
            writer.WriteLine("using System;");
            writer.Write("namespace ");
            writer.WriteLine(parameters["Namespace"]);
            writer.WriteLine('{');
            writer.WriteLine("[System.Diagnostics.DebuggerNonUserCode]");

            writer.Write(parameters["Visibility"]);
            writer.Write(" partial class ");
            writer.Write(parameters["Class"]);
            writer.WriteLine(" : ANLP.LexerBase");
            writer.WriteLine('{');
            writer.WriteLine("protected override void Initialize(){");
            foreach (var expression in expressions)
            {
                if (!states.Contains(expression.State))
                {
                    writer.Write("IDictionary<ANLP.LexerHandler, ANLP.TokenHandler> ");
                    writer.Write(expression.State);
                    writer.WriteLine("Expressions = new Dictionary<ANLP.LexerHandler, ANLP.TokenHandler>();");
                    writer.Write("lexExpressions.Add(0, ");
                    writer.Write(expression.State);
                    writer.WriteLine("Expressions);");
                    states.Add(expression.State);
                }
                if (stringOnlyRegex.IsMatch(expression.Regex))
                {
                    writer.Write("AddString(");
                    writer.Write(expression.State);
                    writer.Write("Expressions, \"");
                    writer.Write(stringRegexEscape.Replace(expression.Regex,@"\1"));
                    writer.Write("\", delegate()");
                    writer.Write(expression.Code);
                    writer.WriteLine(");");
                }
                else
                {
                    writer.Write("AddRegex(");
                    writer.Write(expression.State);
                    writer.Write("Expressions, \"\\\\G");
                    writer.Write(expression.Regex.Replace("\\", "\\\\"));
                    writer.Write("\", delegate()");
                    writer.Write(expression.Code);
                    writer.WriteLine(");");
                }
            }
            writer.WriteLine("}");
            writer.Write(parameters["Code"]);
            writer.WriteLine('}');
            writer.WriteLine('}');

        }

        private Dictionary<string, string> parameters = new Dictionary<string, string>();

        private void SetParameter(string name, string value)
        {
            parameters[name] = value;
        }
    }
}
